package org.chen.mvc.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.chen.mvc.annotation.GetMapping;
import org.chen.mvc.annotation.PostMapping;
import org.chen.mvc.annotation.RequestMapping;
import org.chen.mvc.annotation.RequestMethod;
import org.chen.mvc.exception.MethodNotAllowedException;
import org.chen.mvc.exception.NotFoundException;
import org.chen.mvc.factory.ControllerFactory;
import org.chen.spring.ChenApplicationContext;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.Introspector;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;

/**
 * 请求处理
 *
 * @author Chen
 * @since 2023/6/8 14:13
 */
public abstract class RequestHandler {

    private final ControllerFactory controllerFactory = ChenApplicationContext.getBeanFactory().getBean(ControllerFactory.class);

    /**
     * 处理请求
     *
     * @param req  请求
     * @param resp 响应
     * @throws IOException 异常
     */
    public void handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 处理请求 --> 接口、方法、方法参数
        try {
            Object invoking = invoking(req, resp);
            resp.setContentType("text/html; charset=UTF-8");
            ServletOutputStream outputStream = resp.getOutputStream();
            if (invoking == null) {
                outputStream.write("success".getBytes());
            } else {
                ObjectMapper mapper = new ObjectMapper();
                String result = mapper.writeValueAsString(invoking);
                outputStream.write(result.getBytes());
            }
            outputStream.flush();
            outputStream.close();

        } catch (NotFoundException e) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND, "请求路径不存在");
        } catch (MethodNotAllowedException e) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "不支持的请求方法");
        } catch (IllegalAccessException e) {
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "参数格式错误");
        }
    }

    private Object invoking(HttpServletRequest request, HttpServletResponse response) throws InvocationTargetException, IllegalAccessException {
        String path = request.getRequestURI();
        Method method = controllerFactory.getMethod(path);
        if (method == null) {
            throw new NotFoundException();
        }
        checkMethod(method);
        // 获取请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Object[] keyArray = parameterMap.keySet().toArray();
        //定义一个下标用于获取请求参数
        int parameterIndex = 0;
        Parameter[] parameters = method.getParameters();
        Object[] parameterList = new Object[parameters.length];
        for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++) {
            Parameter parameter = parameters[i];
            Class<?> type = parameter.getType();
            if (type == HttpServletRequest.class) {
                parameterList[i] = request;
            } else if (type == HttpServletResponse.class) {
                parameterList[i] = response;
            } else {
                if (keyArray.length == 0) {
                    parameterList[i] = null;
                } else {
                    Object parameterName = keyArray[parameterIndex++];
                    String[] value = parameterMap.get(parameterName);
                    Class<?> parameterType = parameter.getType();
                    //如果形参为字符串，直接获取请求参数中的第1个数据
                    if (parameterType == String.class) {
                        parameterList[i] = value[0];
                    }
                }
            }
        }
        try {
            String simpleName = method.getDeclaringClass().getSimpleName();
            return method.invoke(ChenApplicationContext.getBeanFactory().getBean(Introspector.decapitalize(simpleName)), parameterList);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    void checkMethod(Method method, RequestMethod checkMethod) {
        boolean flag = false;
        for (Annotation declaredAnnotation : method.getDeclaredAnnotations()) {
            RequestMethod[] methods = new RequestMethod[0];
            if (declaredAnnotation instanceof RequestMapping) {
                methods = ((RequestMapping) declaredAnnotation).method();
            } else if (declaredAnnotation instanceof GetMapping || declaredAnnotation instanceof PostMapping) {
                methods = declaredAnnotation.annotationType().getAnnotation(RequestMapping.class).method();
            }
            for (RequestMethod requestMethod : methods) {
                if (requestMethod.equals(checkMethod)) {
                    flag = true;
                    break;
                }
            }
        }
        if (!flag) {
            throw new MethodNotAllowedException();
        }
    }

    abstract void checkMethod(Method method) throws MethodNotAllowedException;
}
